/**
 * #info
 *
 *@author   wuwg
 *@createTime   2018/03/20
 *@updateTime   2018/04/13
 *@description  slider  component  滑块插件
 */
/**
 * #item
 *
 * @type        aty-slider
 * @cn          滑块
 * @nameprop   label
 * @menu        表单
 * @stPlugin    true
 * @stContainer false
 * @children    []
 * @mixins      [./components/mixins/form-item-props.js]
 */
import config from '../../../config/config.js'

// plugin
import {
  on,
  off
} from '../../../utils/dom'
// components
import InputNumber from '../input-number/index.vue'
import Tooltip from '../../notice/tooltip/index.vue'
// mixins
import formItem from '../form-item/form-item.js'
import SliderMarker from "./marker";
// prefix
const prefixCls = config.prefix + '-slider'
// module
const module = {
  name: config.prefix + '-slider',
  // 混入
  mixins: [formItem],
  components: {
    Tooltip,
    InputNumber,
    SliderMarker
  },
  props: {
    /**
     * #property
     *
     * @name     name
     * @cn      属性名称
     * @group   基本属性
     * @type    string
     * @description 值对应的属性名
     */
    name: {
      type: String
    },
    /**
     * #property
     *
     * @name    value
     * @cn      值
     * @group   基本属性
     * @type    number,array
     * @bind    must
     * @value  0
     * @description  slider滑块选定的值普通模式下，数据格式为数字Number类型，在双滑块模式下，数据格式为长度是2的数组Array，且每项都为数字
     */
    value: {
      type: [Number, Array],
      default: 0
    },
    /**
     * #property
     *
     * @name     maxNumber
     * @cn       最大值
     * @group   基本属性
     * @type    number
     * @bind    must
     * @value  100
     * @description slider 输入框数值最大值
     */
    maxNumber: {
      type: Number,
      default: 100
    },
    /**
     * #property
     *
     * @name    minNumber
     * @cn      最小值
     * @group   基本属性
     * @type    number
     * @bind    must
     * @value  0
     * @description slider输入框数值最小值
     */
    minNumber: {
      type: Number,
      default: 0
    },
    /**
     * #property
     *
     * @name    step
     * @cn      每次改变的步伐
     * @group   基本属性
     * @type    number
     * @bind    must
     * @value  1
     * @description number inputs 每次改变的步伐，可以是小数
     */
    step: {
      type: Number,
      default: 1
    },
    /**
     * #property
     *
     * @name    range
     * @cn      是否开启双滑块模式
     * @group   控制
     * @type    boolean
     * @value    false
     * @description  是否开启双滑块模式
     */
    range: {
      type: Boolean,
      default: false
    },
    /**
     * #property
     *
     * @name    disabled
     * @cn      是否禁用
     * @group   控制
     * @type    boolean
     * @value   false
     * @description 该滑块是否禁用
     */
    disabled: {
      type: Boolean,
      default: false
    },
    /**
     * #property
     *
     * @name    showInput
     * @cn      是否显示input
     * @group   控制
     * @type    boolean
     * @value   false
     * @description  是否显示input
     */
    showInput: {
      type: Boolean,
      default: false
    },
    /**
     * #property
     *
     * @name    showStops
     * @cn      是否显示间断点
     * @group   控制
     * @type    boolean
     * @value   false
     * @description  是否显示间断点，建议在 step 不密集时使用
     */
    showStops: {
      type: Boolean,
      default: false
    },
    /**
     * #property
     *
     * @name    tipFormat
     * @cn      数值格式化函数
     * @group   基本属性
     * @type    function
     * @bind    must
     * @description Slider 会把当前值传给 tip-format，并在 Tooltip 中显示 tip-format 的返回值，若为 null，则隐藏 Tooltip
     */
    tipFormat: {
      type: Function,
      default (val) {
        return val
      }
    },
    /**
     * #property
     *
     * @name    showTip
     * @cn      tip如何显示
     * @group   基本属性
     * @type    list
     * @options [hover, always, never]
     * @value   hover
     * @description tip如何显示，取值为 ['hover', 'always', 'never']中的其中一个，默认hover时显示
     */
    showTip: {
      type: String,
      default: 'hover',
      validator(value) {
        return Artery.isOneOf(value, ['hover', 'always', 'never'])
      }
    },
    /**
     * #property
     *
     * @name    marks
     * @cn      标记
     * @group   基本属性
     * @type    string
     * @description 标记， key 的类型必须为 number 且取值在闭区间 [min, max] 内，每个标记可以单独设置样式。比如{0: '0°C', 50 : '50°C'}
     */
    marks: {
      type: Object
    }
  },
  data() {
    // 检测是否是数组
    let _val = this.value
    _val = Array.isArray(_val) ? this.checkLimits(_val[0], _val[1]) : this.checkLimits(_val)
    return {
      prefixCls: prefixCls,
      currentValue: _val,
      dragging: false,
      pointerDown: '',
      startX: 0,
      currentX: 0,
      startPos: 0,
      oldValue: _val.slice(),
      valueIndex: {
        min: 0,
        max: 1
      },
      scope: {
        className: config.prefix + '-slider'
      },
      inputNumberValue: _val
    }
  },
  watch: {
    value(val) {
      // 检测是否是数组
      var _val = Array.isArray(val) ? this.checkLimits(val[0], val[1]) : this.checkLimits(val)
      if (_val[0] !== this.currentValue[0] || _val[1] !== this.currentValue[1]) {
        this.currentValue = _val
      }
    },
    // 导出的值
    exportValue(values) {
      this.$nextTick(() => {
        this.$refs.minTooltip.updatePopper()
        if (this.range) {
          this.$refs.maxTooltip.updatePopper()
        }
      })
      const value = this.range ? values : values[0]
      this.inputNumberValue = Array.isArray(value) ? value : [value]
      // 双线绑定
      this.$emit('input', value)
      this.validate()
    }
  },
  computed: {
    formItemWarpStyles() {
      return Object.assign(this.formItemStyles, {
        'position': 'relative'
      })
    },
    // 导出的值
    exportValue() {
      const decimalCases = (String(this.step).split('.')[1] || '').length

      return this.currentValue.map(function (item) {
        return Number(item.toFixed(decimalCases))
      })
    },
    classes() {
      return [
        `${prefixCls}`,
        {
          [`${prefixCls}-input`]: this.showInput && !this.range,
          [`${prefixCls}-range`]: this.range,
          [`${prefixCls}-disabled`]: this.disabled
        }
      ].concat(this.formItemClasses)
    },
    minButtonClasses() {
      return [
        `${prefixCls}-button`,
        {
          [`${prefixCls}-button-dragging`]: this.pointerDown === 'min'
        }
      ]
    },
    maxButtonClasses() {
      return [
        `${prefixCls}-button`,
        {
          [`${prefixCls}-button-dragging`]: this.pointerDown === 'max'
        }
      ]
    },
    // 最小值的位置
    minPosition() {
      const val = this.currentValue
      return (val[0] - this.minNumber) / this.valueRange * 100
    },
    // 最大值的位置
    maxPosition: function () {
      const val = this.currentValue
      return (val[1] - this.minNumber) / this.valueRange * 100
    },
    // 进度条样式
    barStyle() {
      const style = {
        width: (this.currentValue[0] - this.minNumber) / this.valueRange * 100 + '%'
      }

      if (this.range) {
        style.left = (this.currentValue[0] - this.minNumber) / this.valueRange * 100 + '%'
        style.width = (this.currentValue[1] - this.currentValue[0]) / this.valueRange * 100 + '%'
      }

      return style
    },
    // 间断点
    stops() {
      const stopCount = this.valueRange / this.step
      const result = []
      const stepWidth = 100 * this.step / this.valueRange
      for (let i = 1; i < stopCount; i++) {
        result.push(i * stepWidth)
      }
      return result
    },
    //
    sliderWidth() {
      return parseInt(Artery.getStyle(this.$refs.slider, 'width'), 10)
    },
    tipDisabled() {
      return this.tipFormat(this.currentValue[0]) === null || this.showTip === 'never'
    },
    valueRange() {
      return this.maxNumber - this.minNumber
    },
    markList() {
      if (!this.marks) return [];

      const marksKeys = Object.keys(this.marks);
      return marksKeys
        .map(parseFloat)
        .sort((a, b) => a - b)
        .filter(point => point <= this.maxNumber && point >= this.minNumber)
        .map(point => ({
          point,
          position: ((point - this.minNumber) * 100) / (this.maxNumber - this.minNumber),
          mark: this.marks[point]
        }));
    }
  },
  methods: {
    // 检测最大最小值
    checkLimits(min, max) {
      if (max) {
        let _min = Math.min(min, max)
        _min = Math.max(this.minNumber, _min)
        let _max = Math.max(min, max)
        _max = Math.min(this.maxNumber, _max)
        return [_min, _max]
      } else {
        let _min = Math.max(this.minNumber, min)
        _min = Math.min(this.maxNumber, _min)
        return [_min]
      }
    },
    // 获取当前的值
    getCurrentValue(event, type) {
      if (this.disabled) {
        return
      }
      const index = this.valueIndex[type]
      if (typeof index === 'undefined') {
        return
      }
      return this.currentValue[index]
    },
    onKeyLeft(event, type) {
      const value = this.getCurrentValue(event, type)
      if (Number.isFinite(value)) {
        this.changeButtonPosition(value - this.step, type)
      }
    },
    onKeyRight(event, type) {
      const value = this.getCurrentValue(event, type)
      if (Number.isFinite(value)) {
        this.changeButtonPosition(value + this.step, type)
      }
    },
    onPointerDown(event, type) {
      if (this.disabled) return
      event.preventDefault()
      this.pointerDown = type
      this.onPointerDragStart(event)
      on(window, 'mousemove', this.onPointerDrag)
      on(window, 'touchmove', this.onPointerDrag)
      on(window, 'mouseup', this.onPointerDragEnd)
      on(window, 'touchend', this.onPointerDragEnd)
    },
    getPointerX(e) {
      return e.type.indexOf('touch') !== -1 ? e.touches[0].clientX : e.clientX
    },
    onPointerDragStart(event) {
      this.dragging = false
      this.startX = this.getPointerX(event)
      this.startPos = (this[`${this.pointerDown}Position`] * this.valueRange / 100) + this.minNumber
    },
    onPointerDrag(event) {
      this.dragging = true
      this.$refs[`${this.pointerDown}Tooltip`].visible = true
      this.currentX = this.getPointerX(event)
      const diff = (this.currentX - this.startX) / this.sliderWidth * this.valueRange

      this.changeButtonPosition(this.startPos + diff)
    },
    onPointerDragEnd() {
      if (this.dragging) {
        this.dragging = false
        this.$refs[`${this.pointerDown}Tooltip`].visible = false
        this.emitChange()
      }
      this.pointerDown = ''
      off(window, 'mousemove', this.onPointerDrag)
      off(window, 'touchmove', this.onPointerDrag)
      off(window, 'mouseup', this.onPointerDragEnd)
      off(window, 'touchend', this.onPointerDragEnd)
    },
    changeButtonPosition(newPos, forceType) {
      const type = forceType || this.pointerDown
      const index = type === 'min' ? 0 : 1
      if (type === 'min') {
        newPos = this.checkLimits(newPos, this.maxPosition)[0]
      } else {
        newPos = this.checkLimits(this.minPosition, newPos)[1]
      }
      const modulus = newPos % this.step
      const value = this.currentValue
      value[index] = newPos - modulus
      this.currentValue = [...value]
      if (!this.dragging) {
        if (this.currentValue[index] !== this.oldValue[index]) {
          this.emitChange()
          this.oldValue[index] = this.currentValue[index]
        }
      }
    },
    emitChange() {
      const value = this.range ? this.exportValue : this.exportValue[0]
      this.$emit('input', value)

      // let event = window.event || arguments.callee.caller.arguments[0]
      /**
       * #event
       *
       * @name    change
       * @param   value   当前的值
       * @param   name    当前传入的props中的name
       * @description   值改变时候触发
       * @body
       */
      this.$emit('change', value, this.name)
      this.validate()
    },
    sliderClick(event) {
      if (this.disabled) return
      const currentX = this.getPointerX(event)
      const sliderOffsetLeft = this.$refs.slider.getBoundingClientRect().left
      const newPos = ((currentX - sliderOffsetLeft) / this.sliderWidth * this.valueRange) + this.minNumber
      if (!this.range || newPos <= this.minPosition) this.changeButtonPosition(newPos, 'min')
      else if (newPos >= this.maxPosition) this.changeButtonPosition(newPos, 'max')
      else this.changeButtonPosition(newPos, ((newPos - this.firstPosition) <= (this.secondPosition - newPos)) ? 'min' : 'max')
    },
    handleInputChange() {
      this.currentValue = this.inputNumberValue
      this.emitChange()
    },
    handleFocus(type) {
      this.$refs[`${type}Tooltip`].handleShowPopper()
    },
    handleBlur(type) {
      this.$refs[`${type}Tooltip`].handleClosePopper()
    },
    // 覆盖formItem内方法
    getValidateValue() {
      const value = this.range ? this.exportValue : this.exportValue[0]
      return value === undefined ? null : value
    }
  },
  updated() {

  },
  created() {

  },
  mounted() {
    // #2852
    this.$on('on-visible-change', (val) => {
      if (val && this.showTip === 'always') {
        this.$refs.minTooltip.doDestroy()
        if (this.range) {
          this.$refs.maxTooltip.doDestroy()
        }
        this.$nextTick(() => {
          this.$refs.minTooltip.updatePopper()
          if (this.range) {
            this.$refs.maxTooltip.updatePopper()
          }
        })
      }
    })
  }
}
export default module
